/**************************************************************
Secretsender
FileName:seretsender
Author:杨良菊
ID:2020240381012
Version:3.0
Date:20220708
Description:该程序通过
**************************************************************/

#include<winsock2.h>//winsock的头文件
#include"Init_Sock.h"
#include <stdio.h>
#include <cstring>
#include<cmath>
#include<iomanip>
#include<iostream>
using namespace std;


char* Co = { 0 };//操作指令
char* Ip = { 0 };//ip地址
char* Op = { 0 };//操作指令
char* data = { 0 };//明文字符

typedef int socklen_t;
#define LINE  10
#define max 1000
//使用枚举类型定义协议中的各种消息类型，可读性好
enum MsgType{
	SENDPAR=1, //发送共享参数
	PAROK, //确认收到共享参数
	MyNum, //发送计算后的参数
	Bye //共享密钥交换完成
};

//定义协议的数据结构	
typedef struct _DH_PROTOCOL
{
	unsigned char version; //版本 
	enum MsgType type;   //消息类型
	unsigned short p; //参数p
	unsigned short a; //参数a
	unsigned short YAB; //共享给对方的数值
	char op[4];  //0不加密 1加密
	//unsigned int datasize; //此例子时一个字符串，字符串有结束符，可以不要这个定义
	char data[max];
}DH_Protocol;


void perror(const char *s);
int close(int fd);

int Server(DH_Protocol se);
int Client(DH_Protocol se,char *ip);
char getkey(DH_Protocol se);
using namespace std;
int mod(int x,int y,int z){
	int s =pow(x,y);
	return(s%z);
}
/***************************************************************
Function Name:getkey
Parameters:无 
Return：int ka（公钥） 
Description:利用Diffie——Hellman密钥交换，求出公钥 
***************************************************************/
char getkey(DH_Protocol se)
{
	//int a=5,p=23;
//	int Xa=rand()%100;
	//int Xb=rand()%100;
	int Xa=12;
	int Xb=13; 
	int Ya = mod(se.a,Xa,se.p);  //Ya代表Alice的公钥
	int Yb = mod(se.a,Xb,se.p);  //Yb代表Bob的公钥
	//Ka,kb代表Alice和Bob两个人之间的共享密钥，因此 Ka = Kb
	int Ka =mod(Yb,Xa,se.p);
	int Kb =mod(Ya,Xb,se.p);
	//se.key=(char)Ka;
	//system("pause");
	return Ka;
}


/***************************************************************
Function Name:getip
Parameters:char* ip(类型，名称)，int size(类型，名称) 
Return：无
Description:获取本机ip 
***************************************************************/
char getip(char* ip,int size)
{
	WORD v = MAKEWORD(1, 1); 
	WSADATA wsaData; 
	WSAStartup(v, &wsaData); 
	// 加载套接字库 
	struct hostent *phostinfo = gethostbyname("");
	char *p = inet_ntoa (* ((struct in_addr *)(*phostinfo->h_addr_list)) );      
	strncpy(ip, p, size - 1); 
	ip[size - 1] = '\0'; 
	WSACleanup( );
	return 0;
	
 } 
 

 
/***************************************************************
Function Name:Server
Parameters:无
Return：无
Description:创建TCP服务器实现服务器和客户端的通信
***************************************************************/
int Server(DH_Protocol se)
{
	//初始化winsock2.2相关的动态库
	Init_Sock i;//实例化对象，调用无参函数
	i.Init(1,1);//调用 初始化函数  
	 
	//获取本机ip 
	// 静态库
	
	printf("hello!\n");
	char localip[20];
	int size=sizeof(localip); 
	getip(localip,size); 
  
	//1.创建TCP Socket，流式套接字
	SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (s == INVALID_SOCKET) {
		cout << "socket error:" << WSAGetLastError() << endl;
		return 0;
	}

	//2.绑定socket到一个IP地址和端口
	sockaddr_in addr;//服务器地址
	//服务器套接字地址
	addr.sin_port = htons(27013);//网络字节序，设置端口号为27013
	addr.sin_addr.S_un.S_addr = inet_addr(localip);//网络字节序，服务器ip地址
	addr.sin_family = AF_INET;//地址族

	int len = sizeof(sockaddr_in);
	//调用blind()函数绑定套接字，不等于SOCKET_ERROR，说明函数调用成功，否则失败
	if (bind(s, (sockaddr*)&addr, len) == SOCKET_ERROR)
	{
		cout << "bind error:" << WSAGetLastError() << endl;
		return 0;
	}

	//3.监听，5代表正在等待完成相应的TCP三路握手过程的队列长度
	//创建一个套接口并监听申请的连接,这里的2代表接受连接的队列长度，listen函数调用成功，则服务器开始监听
	if (::listen(s, 2) == SOCKET_ERROR)
	{
		cout << "listen error:" << WSAGetLastError() << endl;
		return 0;
	}

	//4.循环接受客户端请求，并且返回和客户端通信的套接字
	sockaddr_in addrClient;//保存客户端IP地址端口
	//memset(&addrClient, 0, sizeof(sockaddr_in));
	len = sizeof(sockaddr_in);
	//SOCKET c = accept(s, (sockaddr*)&addrClient, &len);//成功返回套接字
	SOCKET sClient;
	char szText[] = "TCP Server Demo! \t\n";
		cout<<"Wait for the client to connect..."<<endl;
	while (true) 
		{
		//接受一个新连接（accept函数等待客户端的连接，新建套接字）
		sClient = ::accept(s,(SOCKADDR*)&addrClient,&len);
	
		if (sClient == INVALID_SOCKET)
		{
			cout << "accept error:" << WSAGetLastError() << endl;
			return 0;
		}
		printf("Accept a connection：%s \r\n",inet_ntoa(addrClient.sin_addr));
		
		//5,发送，接受消息
		int ret = 1;
		int ssop=1;
		if(ret != SOCKET_ERROR && ret != 0)
		{ 
			int op=0;
			int skey=0;
			char sop[max]={'\0'};
			//调用recv函数接收客户端发送的操作指令 
			ssop = recv(sClient, sop, 2, 0);
			if(strcmp(sop,"-n")==0)
			{
			//调用recv函数接收客户端发送的数据
			char buf[max] = { '\0' };
			ret = recv(sClient, buf, max, 0);//把flag置0，指接收数据缓冲区
			cout << "Receive data：" << buf ;	
			}
		    else if(strcmp(sop,"-m")==0)
			{
				char buf[max] = { '\0' };	
				char ckey[max]={'\0'};
				//调用recv函数接收客户端发送的密钥 
				skey=recv(sClient,ckey,1,0);
				//调用recv函数接收客户端发送的数据
				ret = recv(sClient, buf, max, 0);//把flag置0，指接收数据缓冲区
				cout << "Receive data："<<buf<<endl ;
				int len=strlen(buf);
				cout <<"Data length："<<len<<endl ; 
			    int key=ckey[0];
				char temp_data[max]; 
    			for (int i = 0; i < len; i++)
    			{
        			if (buf[i] >= 128 || buf[i] < 32)
            			continue;
        			temp_data[i] = (buf[i] - key) % 128;
        			if (buf[i] < 32)
            		temp_data[i] = (buf[i] - 32 + 128) % 128;
    			}
				printf("Plain: %s", temp_data);
			}
			else
			{
				printf("Other errors！");
			} 
				
		}
				
		//6.关闭TCP连接
		shutdown(sClient, SD_SEND);
		exit(0);
	}
		if(SOCKET_ERROR)
		{
			cout<<"connect error!";
		}
	//关闭soocket
	closesocket(s);
	//清理winsock环境
	WSACleanup();
    return 0;
	exit(0);
}

/***************************************************************
Function Name:Client
Parameters:char* ip(类型，名称)
Return：无
Description:创建TCP服务器实现服务器和客户端的通信
***************************************************************/
int Client(DH_Protocol se,char *ip)
{
    //初始化winsock2.2相关的动态库
	Init_Sock i;//实例化对象，调用无参函数
	i.Init(1,1);//调用 初始化函数
	
	char localip[20];
	int size=sizeof(localip); 
	getip(localip,size); 
	
	
	//1.创建TCP Socket，流式套接字
	SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(s==INVALID_SOCKET&&strcmp(ip, localip)!=0) 
	{
		cout<<"socket error:"<<WSAGetLastError()<<endl;
		return 0;
	}

	//2.链接服务器
	sockaddr_in addr;//不建议使用sockaddr,建议使用sockaddr_in
	//设置服务器地址
	addr.sin_port=htons(27013);//网络字节序
	//addr.sin_addr.S_un.S_addr=inet_addr("192.168.137.1");//网络字节序
	addr.sin_addr.S_un.S_addr=inet_addr(ip);//网络字节序
	addr.sin_family=AF_INET;//地址族
	int len=sizeof(sockaddr_in);
	int dalen=strlen(data);
	
	if(connect(s,(sockaddr*)&addr,len)==SOCKET_ERROR) {
		cout<<"connect error:"<<WSAGetLastError()<<endl;
		return 0;
	}
	
	//3.接受发送消息
	int ret=1;
	int skey=1;
	int op=1;
	op=send(s,se.op,strlen(se.op),0);
	if(ret!=SOCKET_ERROR&&ret!=0) 
	{	
		
		if(dalen>0)
		{
			if(strcmp(se.op,"-n")==0)
			{
				ret=send(s,se.data,strlen(se.data),0);
				cout<<"Connect success！"<<endl;
				cout<<"Data length：" <<dalen<<endl; 
				cout<<"Plain:"<<se.data;
			}
			else if(strcmp(se.op,"-m")==0)
			{
				cout<<"Connect success！"<<endl;
				int key=getkey(se);
				char temp_data[max];
				char ckey[max]={'\0'};
				ckey[0]=key;
				for (int i = 0; i < strlen(data); i++)
    			{
        			if (data[i] >= 128 || data[i] < 32)
           				continue;
        			temp_data[i] = (data[i] + key) % 128;
        			if (data[i] < 32)
            		temp_data[i] = data[i] + 32;
    			}
				printf("Plain：%s\n",data);
				printf("Data length：%d\n",strlen(data));
				printf("Cipher(hex)："); 
				for(int i=0;i<strlen(data);i++)
    			{
    				printf("%x ", temp_data[i]);
				}
    			printf("\n");
    			printf("Send Cipher："); 
				for(int i=0;i<strlen(data);i++)
    			{
    				printf("%x ", temp_data[i]);
				}
    			printf("\n");
    			skey=send(s,ckey,strlen(ckey),0);
				ret=send(s,temp_data,strlen(temp_data),0);
			}	
		}	
		else
		{
			cout<<"Error！Data length is zero!";
		}		
	}
	else if (SOCKET_ERROR)
	{
		cout<<"connect error!";
		exit(0);
	}
  
	//4.关闭套接字
	closesocket(s);

	//清理winsock环境
	WSACleanup();
	return 0;

}

int main(int argc,char *args[])
{
	DH_Protocol se;
	se.version=3;	
	se.p=23;
	se.a=5;

	/*从命令行获取文件名和要处理的操作*/
	Co = args[1];   //-s表示服务器，-c表示客户端
	Ip = args[2];   //IP地址
	Op = args[3];   //-n表示明文，-m表示加密参数
	data= args[4];

	if (strcmp(Co, "-s") == 0)
	{
		Server(se);
	}
	else if (strcmp(Co, "-c") == 0)
	{
		strcpy(se.op,Op);
		strcpy(se.data,data);
		se.type=SENDPAR;
		Client(se,Ip);
		
	}
	
	
	else      
		printf("Command error\n");
}
